﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Yz.Base;
using Yz.Base.ParaModels;
using Yz.Core.Extensions;
using Yz.Core.Tools;

namespace Yz.FileService.Middlewares
{
    /// <summary>
    /// 分片上传（大文件上传）
    /// </summary>
    public class ChunkUploadMiddleware : BaseUploadMiddleware, IMiddleware
    {


        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            context.Response.ContentType = "application/json; charset=utf-8";
            if (context.Request.Method.Equals(HttpMethods.Options, StringComparison.OrdinalIgnoreCase))
            {
                base.AddHttpOptionHeader(context);
            }
            else if (context.Request.Method.Equals(HttpMethods.Post, StringComparison.OrdinalIgnoreCase))
            {
                #region 验证授权
                await ValidateTokenAsync(context);
                context.Request.Headers.TryGetValue("appId", out StringValues appId);
                #endregion

                #region 分片参数判断
                ///获取参数
                StringValues sv = context.Request.Form["application/json"];
                if (sv == StringValues.Empty)
                {
                    sv = context.Request.Form["data"];
                }
                ParaChunkFile paraChunkFile = null;
                if (sv != StringValues.Empty)
                {
                    paraChunkFile = JsonConvert.DeserializeObject<ParaChunkFile>(sv);
                }
                if (paraChunkFile == null || paraChunkFile.Chunk <= 0 || paraChunkFile.Chunks < 1 || paraChunkFile.Chunk > paraChunkFile.Chunks || string.IsNullOrEmpty(paraChunkFile.FileName) || string.IsNullOrEmpty(paraChunkFile.Guid) || paraChunkFile.FileName.IndexOf(".") <= 0)
                {
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        msg = "文件分片信息不正确"
                    }.ToJson());
                    return;
                }
                #endregion 

                #region 文件验证
                //获取上传的文件
                var file = context.Request.Form.Files.FirstOrDefault();
                if (file == null || file.Length == 0)
                {
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        msg = "上传文件不能为空！"
                    }.ToJson());
                    return;
                }
                //后缀验证
                var ext = Path.GetExtension(paraChunkFile.FileName).ToLower();
                var fileLimit = _uploadConfig.Limits.Where(p => p.Value.AllowExts.Contains(ext)).FirstOrDefault();
                if (string.IsNullOrEmpty(ext) || string.IsNullOrEmpty(fileLimit.Key))
                {
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        msg = "不支持此文件类型！"
                    }.ToJson());
                    return;
                }
                ///判断是否分片
                if (!fileLimit.Value.IsChunk)
                {
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        msg = "此文件类型不支持分片上传"
                    }.ToJson());
                    return;
                }
                //大小验证
                if (file.Length > fileLimit.Value.ChunkSize)
                {
                    int fileSize = (fileLimit.Value.Size / 1024).ToInt();
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        msg = $"此文件类型单片不能超过{fileSize}M"
                    }.ToJson());
                    return;
                }
                #endregion

                var chunkFilename = GetChunkFilename(_uploadConfig.PhysicalPath, appId, paraChunkFile.Chunk, paraChunkFile.Guid);
                try
                {
                    using (var fileStream = File.OpenWrite(chunkFilename))
                    {
                        var stream = file.OpenReadStream();
                        stream.CopyTo(fileStream);
                        fileStream.Flush(true);
                    } 
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        suc = true,
                        msg = "上传完成，请继续上传"
                    }.ToJson());
                    return;
                }
                catch (Exception ex)
                {
                    YzNLog.Error(ex);
                    await context.Response.WriteAsync(new VmUpFileBase()
                    {
                        msg = "上传失败"
                    }.ToJson());
                    return;
                }
            }
            else
            {
                await base.AddHttpNotAllowedAsync(context);
            }
        }

        /// <summary>
        /// 获取分块文件名
        /// </summary>
        /// <param name="physicalPath"></param>
        /// <param name="chunkNumber"></param>
        /// <param name="identifier"></param>
        /// <returns></returns>
        private string GetChunkFilename(string physicalPath, string appId, int chunkNumber, string identifier)
        {
            var temp = Path.Combine(physicalPath, appId, tempFolder, identifier);
            if (!Directory.Exists(temp))
            {
                Directory.CreateDirectory(temp);
            }
            return Path.Combine(physicalPath, appId, tempFolder, identifier, yzTmpFile + "." + chunkNumber);
        }


    }
}
